
package View;

import Controller.Controller;
import com.google.api.services.drive.model.File;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import javax.swing.JFileChooser;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.SwingUtilities;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;

/**
 * Klasa realizuje widok w postaci drzewa plików z dysku zdalnego Google.
 * @param popup - menu dla prawego przycisku myszy(wstaw,usuń...)
 * @param root - korzeń drzewa wymagany przez JTree.
 * @param mi - elementy menu popup.
 * @param currentNode - obecnie używany element drzewa plików(podświetlony).
 * @param searchResults - przechowuje wyniki wyszukiwania.
 * @param controller - referencja na kontroler.
 * @param view - referencja na widok.
 * @param treeScroll - umożliwia scrollowanie w drzewie plików.
 */
public class FilesTree extends JTree implements ActionListener {
        private JPopupMenu popup;
        private JMenuItem mi;
        private final TFolder root;
        private DefaultMutableTreeNode currentNode;
        private Controller controller;
        private View view;
        private JScrollPane treeScroll;
        private ArrayList<DefaultMutableTreeNode> searchResults;
        
        public FilesTree(final Controller controller,final View view, final JScrollPane treeScroll) {
            super( new TFolder(""));
            this.controller = controller;
            this.view = view;
            this.treeScroll = treeScroll;
            root = (TFolder)this.getModel().getRoot();
            currentNode = root;
            
            popup = new JPopupMenu();
            mi = new JMenuItem("Wyślij plik");
            mi.setToolTipText("Dodaje plik w katalogu obecnie zaznaczonym");
            
            mi.addActionListener(this);
            mi.setActionCommand("insert");
            popup.add(mi);
            mi = new JMenuItem("Utwórz katalog");
            mi.setToolTipText("Tworzy pusty katalog.");
            mi.addActionListener(this);
            mi.setActionCommand("create");
            popup.add(mi);
            mi = new JMenuItem("Zmień nazwę");
            mi.setToolTipText("Tworzy pusty katalog.");
            mi.addActionListener(this);
            mi.setActionCommand("rename");
            popup.add(mi);
            mi = new JMenuItem("Usuń");
            mi.addActionListener(this);
            mi.setActionCommand("remove");
            popup.add(mi);
            mi = new JMenuItem("Pobierz");
            mi.addActionListener(this);
            mi.setActionCommand("download");
            popup.add(mi);
            
            popup.setOpaque(true);
            popup.setLightWeightPopupEnabled(true);
            this.setEnabled(false);
        
            addMouseListener( new MouseAdapter() { 
                 @Override
                    public void mouseClicked(MouseEvent e) {
                        if (SwingUtilities.isRightMouseButton(e)) {
                             int row = getClosestRowForLocation(e.getX(), e.getY());
                                if(view.isZalogowany()){
                                    setSelectionRow(row);
                                    DefaultMutableTreeNode node;       
                                    TreePath path = getSelectionPath();
                                    node = (DefaultMutableTreeNode) path.getLastPathComponent();
                                    currentNode = node;
                                    popup.show(e.getComponent(), e.getX(), e.getY());
                                }    
                         }
                    }
             });
        }
        /**
         * Odświeża strukturę drzewa, wymagana po dokonaniu zmian.
         */
        public void refreshTree() {
           ((DefaultTreeModel )this.getModel()).nodeStructureChanged((TreeNode)root);
           this.scrollPathToVisible(new TreePath(currentNode.getPath()));
           this.setSelectionRow(this.getRowForPath(new TreePath(currentNode.getPath())));  
        }
        /**
         * @return zwraca listę plików tworzących drzewo, pomocna przy przeszukiwaniu struktury.
         */
        public Enumeration getTreeNodes() {
           return root.breadthFirstEnumeration();
        }
        /**
         * Ustawia aktualny węzeł.
         * @param node - aktualny węzeł.
         */
        public void setCurrentNode(DefaultMutableTreeNode node) {
           this.currentNode = node; 
        }
        /**
         * @return zwraca referencję na aktualny węzeł.
         */
        public DefaultMutableTreeNode getCurrentNode() {
           return this.currentNode; 
        }
        /**
         * Funckja buduje drzewo na podstawie listy plików zwróconych po połączeniu z dyskiem zdalnym.
         */
        public void initTree() {
        List<File> files = controller.doGetFilesList();
          for(File f : files)  {
           if(!f.getLabels().getTrashed() && !f.getLabels().getHidden()) {
                if( (f.getParents().isEmpty() ) || (f.getParents().size() > 0 && f.getParents().get(0).getIsRoot() == true) ) {
                     if(f.getMimeType().equals("application/vnd.google-apps.folder")) {
                      TFolder new_folder = new TFolder(f.getTitle());
                      root.add(new_folder);    
                      recursiveInsert(new_folder, files);
                     }
                     else
                     {
                      root.add(new TFile(f.getTitle()));  
                      
                     }                       
                }  
           }      
          }
         refreshTree();
        }
        /**
         * Rekurencyjna funkcja pomocnicza do wstawiania węzłów w drzewie.
         * @param parent - katalog rodzica.
         * @param files - lista plików.
         */
        public void recursiveInsert(TFolder parent, List<File> files) { 
          for(File f : files)  { 
           if(!f.getLabels().getTrashed()) {   
             if( (f.getParents().size() > 0) &&
                 GetFileID(f.getParents().get(0).getId(),files) != null && 
                 GetFileID(f.getParents().get(0).getId(),files).equals(parent.toString())
               ) {
               if(f.getMimeType().equals("application/vnd.google-apps.folder")) {
                 TFolder next_parent = new TFolder(f.getTitle());
                 parent.add(next_parent);
                 recursiveInsert(next_parent, files);
               }
               else {
                TFile new_file = new TFile(f.getTitle());
                parent.add(new_file);
               }
                 
             }
           }  
          } 
        }   
        /**
         * Znajduje węzeł w drzewie na podstawie nazwy (dopasowanie zawierające frazę).
         * @param name - nazwa węzła.
         */
        public void find(String name) {
          Enumeration item = this.getTreeNodes();
          searchResults = new ArrayList<>();
            while(item.hasMoreElements()) {
             DefaultMutableTreeNode node = (DefaultMutableTreeNode)item.nextElement();
               if(node.getUserObject().toString().indexOf(name) != -1) {
                 searchResults.add(node);
               }
            } 
          if(searchResults.size() <= 0) {
            JOptionPane.showMessageDialog(view, "Nie znaleziono plikow folderow zawierajacych podana fraze.", "Rezultat wyszukiwania", 1);
          }
          else {
            currentNode = searchResults.get(0);
            this.scrollPathToVisible(new TreePath(currentNode.getPath()));
            this.setSelectionRow(this.getRowForPath(new TreePath(currentNode.getPath()))); 
          }
        }
        /**
         * Znajduje węzeł w drzewie na podstawie nazwy (dopasowanie nazwa węzła jest dokładnie równa szukanej frazie).
         * @param name - nazwa do wyszukania.
         * @return prawda gdy udało się znaleźć taki węzeł , falsz w przeciwnym przypadku.
         */
        public boolean findEqual(String name) {
          boolean found = false;
          Enumeration item = this.getTreeNodes();
            while(item.hasMoreElements()) {
             DefaultMutableTreeNode node = (DefaultMutableTreeNode)item.nextElement();
               if(node.getUserObject().toString().equals(name)) {
                 found = true;
               }
            } 
          return found;
        }
        /**
         * Znajduje kolejny węzeł zawierający podaną frazę.
         * @param name - nazwa do wyszukiania
         */
        public void findNext(String name) { 
         int pos = -1;
         int next_pos = -1;
         int count = 0;
         if(searchResults != null && currentNode.getUserObject().toString().indexOf(name) != -1) {
            for(DefaultMutableTreeNode node : searchResults) {
                if(pos != -1) {
                    if(node.getUserObject().toString().indexOf(name) != -1) {
                        if(next_pos == -1) {
                            next_pos = count;
                            break;
                        }
                    }
                }
                else {
                   if(node.getUserObject().toString().equals(currentNode.getUserObject().toString())) {
                       pos = count;
                   }
                }
                count++;
            }
            if(pos == -1) {
                find(name);
            }
            else if(next_pos == -1) {
                JOptionPane.showMessageDialog(view, "Nie znaleziono kolejnego pliku lub folderu zawierajacego podana fraze.", "Rezultat wyszukiwania", 1);
            }
            else {
                currentNode = searchResults.get(next_pos);
                this.scrollPathToVisible(new TreePath(currentNode.getPath()));
                this.setSelectionRow(this.getRowForPath(new TreePath(currentNode.getPath()))); 

            }
         }
         else {
            find(name);     
         }
            
        }
        /**
         * Czyści całe drzewo.
         */
        public void removeTree() {
            root.removeAllChildren();
            refreshTree();
        }
        /**
         * Pobiera id danego węzła.
         * @param filename - nazwa węzła.
         * @param results - lista plików, w której znajduje się ten węzeł.
         * @return id węzła.
         */
        public String GetFileID(String filename, List<File> results) {
            for (File f : results) {
                if(f.getId().equals(filename)) {
                  return f.getTitle();
                }
            }   
          return null;
        }    
        
        
        @Override
        public void actionPerformed(ActionEvent e) {
            DefaultMutableTreeNode node;       
            TreePath path = this.getSelectionPath();
            node = (DefaultMutableTreeNode) path.getLastPathComponent();
             if(e.getActionCommand().equals("insert")) {
                JFileChooser filechooser = new JFileChooser();
                int ret_code = filechooser.showDialog(treeScroll, "Wyślij plik");
                if(ret_code == JFileChooser.APPROVE_OPTION) {
                    if( filechooser.getSelectedFile().canRead() ) {
                      String parent_id;  
                      if(node.isLeaf()) {
                        node = (TFolder)node.getParent();
                      }
                        parent_id = controller.doGetFileID(node.getUserObject().toString());
                        if (controller.doInsertFile(filechooser.getSelectedFile(), parent_id) ) {
                            TFile new_node = new TFile(filechooser.getSelectedFile().getName());
                            node.add(new_node);
                            currentNode = new_node;
                        }           
                    }
                    else {
                      view.showError("Nie udało się odczytać podanego pliku.");
                      return;  
                    }
                }

             }
             else if(e.getActionCommand().equals("create")) {
                 String directory_name; 
                 directory_name = JOptionPane.showInputDialog(treeScroll, "Wprowadź nową nazwę katalogu.");
                 if(directory_name != null && !directory_name.equals("")) {
                   String parent_id;    
                   if(node.isLeaf()) {
                        node = (TFolder)node.getParent();
                   }
                   parent_id = controller.doGetFileID(node.getUserObject().toString());
                   if(controller.doInsertFolder(directory_name, parent_id)) {
                        TFolder new_node = new TFolder(directory_name);
                        node.add(new_node);  
                        currentNode = new_node;
                   }
                 }
                 else {
                   view.showError("Nazwa folderu nie może być pustym ciągiem.");  
                 }
             }
             else if(e.getActionCommand().equals("rename")) {
                 if(node.isRoot()) {
                   view.showError("Nie można zmienić nazwy korzenia.");
                   return;
                 }
                 String new_name; 
                 new_name = JOptionPane.showInputDialog(treeScroll, "Wprowadź nową nazwę.");
                 if(new_name != null && !new_name.equals("")) {
                   if(controller.doRenameFile(node.getUserObject().toString(), new_name)) {
                         node.setUserObject(new_name);
                         currentNode = node;
                     }
                 }
                 else {
                   view.showError("Nazwa nie może być pusta!");   
                 }
             }
             else if(e.getActionCommand().equals("download")) {
                 if(node.isRoot()) {
                   view.showError("Nie można pobrać korzenia!");
                   return;
                 } 
                 else if(!node.isLeaf()) {
                   view.showError("Niedozwolone pobieranie całych katalogów");  
                 }
                 else if(controller.doGetDownloadPath() != null && !controller.doGetDownloadPath().equals("")) {
                   controller.doDownloadFile(node.getUserObject().toString());  
                   currentNode = node;
                 }
                     
                 else {
                   view.showError("Domyślna ścieżka pobierania nie może być pusta!");    
                 }
             }
             else if(e.getActionCommand().equals("remove")) {
                 if(node.isRoot()) {
                   view.showError("Nie można usunąć korzenia.");
                   return;
                 }
                 if(!node.isLeaf()) {
                  int response = JOptionPane.showConfirmDialog(
                    treeScroll,
                    "Czy na pewno chcesz usunac katalog wraz z cala zawartoscia?",
                    "Uwaga",
                    JOptionPane.YES_NO_OPTION,
                    JOptionPane.WARNING_MESSAGE);
                  if(response==JOptionPane.OK_OPTION) {
                    if(controller.doTrashFile(node.getUserObject().toString())) {  
                      TFolder parent = (TFolder)node.getParent();
                      parent.remove(node);
                      currentNode = parent;
                    }
                  }
                 }
                 else {
                    if(controller.doTrashFile(node.getUserObject().toString())) { 
                      TFolder parent = (TFolder)node.getParent();
                      parent.remove(node);
                      currentNode = parent;
                    }  
                 }
             }
            refreshTree();
         
        }

        
    } 